/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.netbeans.modules.qcubedpalette;

import java.util.Stack;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.StyledDocument;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.api.lexer.TokenUtilities;
import org.netbeans.modules.editor.indent.api.Reformat;
import org.openide.text.NbDocument;
import org.netbeans.modules.php.editor.lexer.LexUtilities;
import org.netbeans.modules.php.editor.lexer.PHPTokenId;

public class PaletteUtilities {

	public static void insertAt(final String s, final JTextComponent target, final int offset) throws BadLocationException {

		final StyledDocument doc = (StyledDocument) target.getDocument();

		class AtomicChange implements Runnable {

			@Override
			public void run() {
				Document value = target.getDocument();
				if (value == null) {
					return;
				}
				try {
						insertAndFormatAtOffset(s, doc, offset);
				} catch (BadLocationException e) {
				}
			}
		}

		try {
			NbDocument.runAtomicAsUser(doc, new AtomicChange());
		} catch (BadLocationException ex) {
		}

	}

	private static int insertAndFormatAtOffset(String s, Document doc, int offset)  throws BadLocationException{
		Position p = doc.createPosition(offset);

		doc.insertString(offset, s, null);

		Reformat r = Reformat.get(doc);
		r.lock();
		r.reformat(offset+1, p.getOffset());
		r.unlock();

		return offset;
	}

	public static int findClassMemberInsertionOffset(Document doc) {

		PHPTokenId[] ClassSeq = {PHPTokenId.PHP_CLASS,
			PHPTokenId.PHP_CURLY_OPEN};

		return PaletteUtilities.parseDocForPHPTokenSequence(doc, ClassSeq);
	}

	public static int findClassMethodInsertionOffset(Document doc) {
		PHPTokenId[] ClassSeq = {PHPTokenId.PHP_CLASS,
			PHPTokenId.PHP_CURLY_OPEN};

		return PaletteUtilities.parseDocForPHPTokenSequence(doc, ClassSeq);

	}

	public static int findFormCreateInsertionOffset(Document doc) {

		PHPTokenId[] FormCreateSeq = {PHPTokenId.PHP_FUNCTION,
			PHPTokenId.PHP_STRING,
			PHPTokenId.PHP_CURLY_OPEN,
			PHPTokenId.PHP_CURLY_CLOSE};

		String[] matchString = {"Form_Create"};

		int offset = PaletteUtilities.parseDocForPHPTokenSequence(doc, FormCreateSeq, matchString);
		return offset-1;
	}

	public static int parseDocForPHPTokenSequence(Document doc, PHPTokenId[] tokenSeq) {
		return PaletteUtilities.parseDocForPHPTokenSequence(doc, tokenSeq, null);
	}

	/**
	 * Parses a document for the specified token sequence and returns the offset at which the last token
	 * was found in the document.  If a string array is specified, it allows you to search for the specified
	 * string within the document anytime a PHP_STRING token is found within the document.
	 *
	 * The token sequence does not specify a sequential sequence, rather it searches for the tokens specified with
	 * any number of tokens in between.  This allows you to, for example, search for the sequence PHP_FUNCTION,
	 * PHP_STRING, PHP_CURLY_OPEN, PHP_CURLY_CLOSE, and it will find the closing curly brace for the function
	 * specified.
	 *
	 * This function is scope sensitive, so if you specify CURLY_OPEN in the token sequence and a CURLY_CLOSE, it will
	 * only return the offset of the closing brace which matches the scope of the open brace.
	 * @param doc The document you would like to parse
	 * @param tokenSeq an array of PHPTokenIds which which you are looking for the offset of in the document
	 * @param matchStrings the strings to match
	 * @return the offset of the last token in the token sequence.
	 */
	public static int parseDocForPHPTokenSequence(Document doc, PHPTokenId[] tokenSeq, String[] matchStrings) {
		TokenSequence<PHPTokenId> ts = LexUtilities.getPHPTokenSequence(doc, 0);
		Stack<PHPTokenId>brackets = new Stack<PHPTokenId>();
		boolean checkScope = false;
		int tokenIndex = 0, strIndex = 0;
		while (ts.moveNext()) {
			if (tokenIndex == tokenSeq.length) {
				break;
			} else if (tokenSeq[tokenIndex] == PHPTokenId.PHP_STRING && ts.token().id() == tokenSeq[tokenIndex]) {
				if (TokenUtilities.equals(ts.token().text(), matchStrings[strIndex])) {
					tokenIndex++;
					strIndex++;
				} else {
					tokenIndex = 0;
				}
			} else if (ts.token().id() == tokenSeq[tokenIndex]) {	// Correct token encountered
				if (tokenSeq[tokenIndex] == PHPTokenId.PHP_CURLY_OPEN) {
					brackets.clear();
					checkScope = true;
				} else if (checkScope && tokenSeq[tokenIndex] == PHPTokenId.PHP_CURLY_CLOSE) {
					if (brackets.empty()) {
						checkScope = false;
					} else {
						continue;
					}
				}
				tokenIndex++;
			} else if(checkScope && ts.token().id() == PHPTokenId.PHP_CURLY_OPEN) {
				brackets.add(PHPTokenId.PHP_CURLY_OPEN);
			} else if(checkScope && ts.token().id() == PHPTokenId.PHP_CURLY_CLOSE) {
				brackets.pop();
			}
		}
		return ts.offset();
	}
}
